package com.hc.programming.thread;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 问题：  1。 有三十个账户。每个账户初始余额10000元。
 * 2。 有二十个转账线程，对三十个账户中的两个随机选取账户进行转账，转账额度100以内随机数，金额保留两位小数点。
 * 3。 每个线程执行100次转账操作。
 * 4。 最后请打印出三十个账户的余额。
 * {@code Author}: 黄超
 * {@code CreateTime}: 2024-12-21 15:42
 * {@code email}: 18008108748@163.com
 * {@code Version}: 1.0
 */
public class 多线程转账多次 {

    /**
     * 账户池
     */
    private static volatile Account[] accounts;

    /**
     * 线程池
     */
    private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(20, 20, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));

    public static void main(String[] args) throws InterruptedException {
        int accountCount = 30; // 账户数
        int transferThreadCount = 20; // 20个线程
        int transferNumber = 100; // 100次转账
        int maxTransferAmount = 100; // 最大转账金额
        String initBalance = "10000.00"; // 初始余额
        // 收款账户初始化
        initAccounts(accountCount, initBalance);
        // 转账线程初始化及执行
        CountDownLatch cdl = new CountDownLatch(transferThreadCount);
        submitTransferThread(transferThreadCount, transferNumber, accountCount, maxTransferAmount, cdl);
        cdl.await();
        // 余额处理
        for (int i = 0; i < accountCount; i++) {
            System.out.println(accounts[i]);
        }
        System.exit(0);
    }

    /**
     * 提交转账线程
     *
     * @param transferThreadCount 线程梳理
     * @param transferNumber      转账次数
     * @param accountCount        账户数量
     * @param maxTransferAmount   最大转账金额
     * @param cdl                 执行控制
     */
    private static void submitTransferThread(int transferThreadCount, int transferNumber, int accountCount, int maxTransferAmount, CountDownLatch cdl) {
        for (int i = 0; i < transferThreadCount; i++) {
            poolExecutor.submit(() -> {
                Random random = new Random();
                int accountIndex;
                for (int j = 0; j < transferNumber; j++) {
                    accountIndex = random.nextInt(accountCount);
                    // 金额  后续可以优化成：使用Long的“分”
                    accounts[accountIndex].transfer(new BigDecimal(random.nextInt(maxTransferAmount)));
                    accountIndex = random.nextInt(accountCount);
                    accounts[accountIndex].transfer(new BigDecimal(random.nextInt(maxTransferAmount)));
                }
                cdl.countDown();
            });
        }
    }

    /**
     * 初始化账户
     *
     * @param accountCount 账户数量
     * @param initBalance  初始余额
     */
    private static void initAccounts(int accountCount, String initBalance) {
        accounts = new Account[accountCount];
        for (int i = 0; i < accountCount; i++) {
            accounts[i] = new Account((long) i, new BigDecimal(initBalance));
        }
    }

    /**
     * 账户
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Account {

        /**
         * 用户ID
         */
        private Long userId;

        /**
         * 余额 后续可以优化成：使用Long的“分”
         */
        private BigDecimal balance;

        /**
         * 转账收款
         */
        public synchronized void transfer(BigDecimal amount) {
            this.balance = this.balance.add(amount);
        }

        @Override
        public String toString() {
            return "Account{" + "userId=" + userId + ", balance=" + balance + '}';
        }
    }

}
